#include "trend.h"
#include "common.h"
#include <cmath>
#include <iomanip>
#include <sstream>

#define PCT_THRESHOLD 0.5
#define PDT_THRESHOLD 0.6
#define PDT_SIGNAL_THRESHOLD 0.9
#define PCT_SIGNAL_THRESHOLD 0.7

#define SPEARMAN_THRESHOLD_UP 0.78
#define SPEARMAN_THRESHOLD_LOW 0.6

#define STD_OWD_VAR_THRESHOLD_UP 15
#define STD_OWD_VAR_THRESHOLD_LOW 5

#define MAX_REC_SIZE 5000
#define MAX_CLUSTER_THRESHOLD 50

#define CORRELATION_THRESHOLD 0.6
#define MIN_CORRELATION_CNT 5

ITrendDetector::ITrendDetector()
{
	m_midMean = 0;
	m_samples.reserve(5000);
	//m_pRec = new uint64[MAX_REC_SIZE];
	//m_idx = 0;
}

ITrendDetector::~ITrendDetector()
{
	//delete []m_pRec;
}

void ITrendDetector::AddSample(uint64 val)
{
	m_samples.push_back(val);
	//if( m_idx < MAX_REC_SIZE ){
	//	m_pRec[m_idx++] = val;
	//}
}

//void ITrendDetector::Clear()
//{
//	m_samples.clear();
//	m_presamples.clear();
//}

void ITrendDetector::Dump()
{
	if( m_samples.empty() ) return;

	std::ostringstream os;
	for(size_t k=0; k<m_samples.size()-1; ++k){
		os<<m_samples[k]<<",";
	}
	os<<m_samples[m_samples.size()-1];

	LOG4CPLUS_DEBUG(g_logger, os.str().c_str());
}

void ITrendDetector::preprocess()
{
	m_presamples.clear();
	m_midMean = 0;

	uint64 min_val = 0;
	uint64 max_val = 0;
	GetMinMax(m_samples, min_val, max_val);	
	for(size_t k=0; k<m_samples.size(); ++k){
		m_samples[k] -= min_val;
	}	

	//for(size_t k=0; k<m_samples.size(); ++k){
	//	m_samples[k] /= 4;
	//}

	int grp_cnt = static_cast<int>(floor(sqrt(static_cast<float>(m_samples.size()))));
	if( grp_cnt < 2 ){
		std::copy(m_samples.begin(), m_samples.end(), std::back_inserter(m_presamples));
	}

	int cnt_per_grp = m_samples.size()/grp_cnt;
	LOG4CPLUS_DEBUG(g_logger, "split data into "<< grp_cnt <<" groups");

	//for(int i=0; i<grp_cnt; ++i){
	//	float avg = 0.0f;
	//	for(int j=0; j<cnt_per_grp; ++j){
	//		avg += m_samples[i*cnt_per_grp + j]/2;
	//	}
	//	avg /= cnt_per_grp;
	//	m_presamples.push_back(avg);
	//}

	for(int i=0; i<grp_cnt; ++i){
		int grp_size = cnt_per_grp;
		if( i == grp_cnt-1 ){
			grp_size = m_samples.size() - i*cnt_per_grp;
		}
		int mid = i*cnt_per_grp + grp_size/2;
		uint64 v = 0;
		if(grp_size%2 == 0){
			v = (m_samples[mid]+m_samples[mid+1])/2;			
		}else{
			v = m_samples[mid];
		}	
		m_midMean += v;
		m_presamples.push_back(v);
	}

	m_midMean /= grp_cnt;
}

//////////////////////////////////////////////////////////////////////////
// PCDTTrendDetector
//////////////////////////////////////////////////////////////////////////
bool PCDTTrendDetector::CalcIncreasingTrend(bool prep)
{
	if( m_samples.size() == 0 ) return false;	

	Dump();

	if(prep && m_samples.size() > MAX_CLUSTER_THRESHOLD){
		preprocess();
	}else{
		m_presamples.clear();
		m_midMean = 0;
		std::copy(m_samples.begin(), m_samples.end(), std::back_inserter(m_presamples));
	}	

	float pct = GetPCT();
	float pdt = GetPDT();

	return ( pct > PCT_THRESHOLD) && ( pdt > PDT_THRESHOLD) || ( pct >= PCT_SIGNAL_THRESHOLD ) || ( pdt >= PDT_SIGNAL_THRESHOLD );
}

float PCDTTrendDetector::GetPCT()
{
	int cnt = 0;
	for(size_t i=0; i<m_presamples.size()-1; ++i){
		if( m_presamples[i+1] > m_presamples[i] ){
			cnt += 1;
		}
	}
	
	float pct = cnt*1.0f/(m_presamples.size()-1);

	LOG4CPLUS_DEBUG(g_logger,"pct = "<< std::setprecision(2)
		<<std::setiosflags(std::ios::fixed) <<pct);
	return pct;
}

float PCDTTrendDetector::GetPDT()
{
	uint64 sum = 0;
	for(size_t i=0; i<m_presamples.size()-1; ++i){
		if( m_presamples[i+1] > m_presamples[i]){
			sum += (m_presamples[i+1] - m_presamples[i]);
		}else{
			sum += (m_presamples[i] - m_presamples[i+1]);
		}
	}

	if( sum == 0 ){
		LOG4CPLUS_DEBUG(g_logger,"pdt = "<< std::setprecision(2)
			<<std::setiosflags(std::ios::fixed) <<0.0);
		return 0;
	}

	float pdt = (m_presamples[m_presamples.size()-1] - m_presamples[0])*1.0f/sum;

	LOG4CPLUS_DEBUG(g_logger,"pdt = "<< std::setprecision(2)
		<<std::setiosflags(std::ios::fixed) <<pdt);
	return pdt;
}

//////////////////////////////////////////////////////////////////////////
bool SpearmanTrendDetector::CalcIncreasingTrend(bool prep)
{
	if(m_samples.empty()) return false;

	Dump();

	if(prep && m_samples.size() > MAX_CLUSTER_THRESHOLD){
		preprocess();

		////DO NOT USE grouped value on spearman
		//m_presamples.clear();
		//std::copy(m_samples.begin(), m_samples.end(), std::back_inserter(m_presamples));
		
		//when there're lots of samples, there will be overflow for mulplication, so should
		//use pre-process to cluster them into groups when there're too many many samples, 
		//and there SHOULD NOT be too many for it may still overflow the result
		//if( m_samples.size() < 2000 ){
		//	m_presamples.clear();
		//	std::copy(m_samples.begin(), m_samples.end(), std::back_inserter(m_presamples));
		//}

		//whether there should be a loop to cluster the samples into small enough groups?

	}else{
		m_presamples.clear();
		m_midMean = 0;
		std::copy(m_samples.begin(), m_samples.end(), std::back_inserter(m_presamples));
	}	

	std::vector<uint64> m_sorted;
	std::copy(m_presamples.begin(), m_presamples.end(), std::back_inserter(m_sorted));

	std::sort(m_sorted.begin(), m_sorted.end());

	std::map<uint64, float> samp_rank_map;

	uint64 m_last = m_sorted[0];
	int m_last_cnt = 1;
	int sum_value = 1;
	for(unsigned i=1; i<m_sorted.size(); ++i){
		if( m_last == m_sorted[i] ){
			++m_last_cnt;
			sum_value += (i+1);
		}else{
			samp_rank_map.insert(std::make_pair(m_last, sum_value*1.0f/m_last_cnt));
			m_last_cnt = 1;
			m_last = m_sorted[i];
			sum_value = i+1;
		}
		if( i == m_sorted.size()-1 ){
			samp_rank_map.insert(std::make_pair(m_last, sum_value*1.0f/m_last_cnt));
		}
	}

	std::vector<float> rank;

	for(unsigned j=0; j<m_presamples.size(); ++j){
		rank.push_back(samp_rank_map[m_presamples[j]]);
	}

	uint64 sum = 0;
	for (unsigned i = 0; i < rank.size(); i++) {
		uint64 difference = rank[i] - (i+1);
		sum += difference*difference;
	}

	double rs = 1 - 6.0*sum/(m_presamples.size()*(m_presamples.size()*m_presamples.size()-1));

	LOG4CPLUS_DEBUG(g_logger,"Spearman = "<< std::setprecision(2)<<std::setiosflags(std::ios::fixed) <<rs
		<<" m_midMean = "<<m_midMean);

	return rs>SPEARMAN_THRESHOLD_UP;
	//if( !(prep && m_samples.size() > MAX_CLUSTER_THRESHOLD) ){
	//	return rs>SPEARMAN_THRESHOLD_UP;
	//}else{
	//	return (rs>SPEARMAN_THRESHOLD_UP && m_midMean>STD_OWD_VAR_THRESHOLD_LOW) || 
	//		(rs>SPEARMAN_THRESHOLD_LOW && m_midMean>STD_OWD_VAR_THRESHOLD_UP);
	//}

}

bool SimPearson::IsCorrelated()
{
	assert(m_x.size() == m_y.size());

	//if( m_x.size() < MIN_CORRELATION_CNT ){
	//	return false;
	//}
	if( m_x.size() == 1 )
		return 1;

	m_preX.clear();
	m_preY.clear();
	std::copy(m_x.begin(), m_x.end(), std::back_inserter(m_preX));
	std::copy(m_y.begin(), m_y.end(), std::back_inserter(m_preY));

	double min_val = 0;
	double max_val = 0;
	GetMinMax(m_preX, min_val, max_val);

	if(min_val != 0){
		for(size_t k=0; k<m_preX.size(); ++k){
			m_preX[k] /= min_val;
		}	
	}

	GetMinMax(m_preY, min_val, max_val);	
	if( min_val !=0 ){
		for(size_t k=0; k<m_preY.size(); ++k){
			m_preY[k] /= min_val;
		}	
	}

	double sum1 = 0.0;
	double sum2 = 0.0;
	double sum1Sq = 0.0;
	double sum2Sq = 0.0;
	double pSum = 0.0;
	uint32 n = m_x.size();

	for(uint32 i=0; i<n; ++i){
		sum1 += m_preX[i];
		sum2 += m_preY[i];
		sum1Sq += m_preX[i] * m_preX[i];
		sum2Sq += m_preY[i] * m_preY[i];
		pSum += m_preX[i] * m_preY[i];
	}
	double num = pSum - (sum1*sum2/n);
	double den = sqrt((sum1Sq-pow(sum1,2)/n) * (sum2Sq-pow(sum2,2)/n));
	if(den == 0)
		return false;
	double r = num/den; 

	Dump();

	LOG4CPLUS_DEBUG(g_logger, "SimPearson r = "<< std::setprecision(2) <<std::setiosflags(std::ios::fixed) <<r);

	return r > CORRELATION_THRESHOLD;
}

void SimPearson::AddSample(uint32 x, uint32 y)
{
	m_x.push_back(x);
	m_y.push_back(y);
}

void SimPearson::Clear()
{
	m_x.clear();
	m_y.clear();
}

void SimPearson::Dump()
{
	if( m_x.empty() ) return;

	std::ostringstream os;
	os<<"<<";
	for(size_t k=0; k<m_x.size()-1; ++k){
		os<<byte_format(m_x[k],'A')<<"/s"<<",";
	}
	os<<byte_format(m_x[m_x.size()-1],'A')<<">>";

	os<<"<<";
	for(size_t k=0; k<m_y.size()-1; ++k){
		os<<m_y[k]<<",";
	}
	os<<m_y[m_y.size()-1]<<">>";

	LOG4CPLUS_DEBUG(g_logger, os.str().c_str());
}